package org.replay.config;

/*
 * Copyright © 2020 lizicong,All rights reserved.
 *
 * Licensed under the GPL License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import com.alibaba.fastjson.JSONObject;
import io.lettuce.core.ReadFrom;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.codec.Utf8StringCodec;
import io.lettuce.core.dynamic.RedisCommandFactory;
import io.lettuce.core.masterslave.MasterSlave;
import io.lettuce.core.masterslave.StatefulRedisMasterSlaveConnection;
import org.replay.repository.RedisDynamicCommands;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

/**
 * Redis集群连接信息配置
 * <p>
 * 奇怪的是,lettuce的集群模式好像还做不了负载均衡,所以一主双从实际上只用了一主一从.
 * 以后自己写个负载均衡器
 * @author lizicong
 */
@Configuration
public class RedisConnectionConfiguration implements BeanPostProcessor {


    @Bean("redisConnectionConfiguration-cfg")
    public JSONObject cfg(@Qualifier("nacos-cfg") JSONObject nacosConfiguration) {
        //截取相应的JSON配置信息中的节点
        return nacosConfiguration.getJSONObject("Redis");
    }

    @Resource
    @Qualifier("redisConnectionConfiguration-cfg")
    JSONObject cfg;

    /**
     * 只写主节点配置信息
     *
     * @return RedisURI
     */
    @Bean("onlyWriteNodeURI")
    public RedisURI dynamicCommandRedisURI() {
        return RedisURI.builder()
                .withSentinel(cfg.getString("SENTINEL-HOST"), Integer.valueOf(cfg.getString("SENTINEL-PORT")))
                .withSentinelMasterId(cfg.getString("SENTINEL-CLUSTER-NAME"))
                //过期时间(单条指令执行的最大时间)
                .withTimeout(Duration.of(Integer.valueOf(cfg.getString("TTL")), ChronoUnit.SECONDS))
                .build();
    }

    /**
     * 只读从节点配置信息
     *
     * @return List<RedisURI>
     */
    @Bean("onlyReadNodeURI")
    public List<RedisURI> defaultRedisURI() {
        List<RedisURI> uriOfSlaves = new ArrayList<>();
        uriOfSlaves.add(RedisURI.builder()
                .withHost(cfg.getString("SLAVE-1-HOST"))
                .withPort(Integer.valueOf(cfg.getString("SLAVE-1-PORT")))
                .withTimeout(Duration.of(Integer.valueOf(cfg.getString("TTL")), ChronoUnit.SECONDS))
                .build());
        uriOfSlaves.add(RedisURI.builder()
                .withHost(cfg.getString("SLAVE-2-HOST"))
                .withPort(Integer.valueOf(cfg.getString("SLAVE-2-PORT")))
                .withTimeout(Duration.of(Integer.valueOf(cfg.getString("TTL")), ChronoUnit.SECONDS))
                .build());
        return uriOfSlaves;
    }

    /**
     * 只写主节点客户端
     * <p>
     * 该客户端使用的是lettuce动态命令模式
     * 为什么使用整个？其实只是尝尝鲜,目前没有发现比RedisCommands原生接口好用的地方
     *
     * @param redisURI 只写主节点连接信息
     * @return RedisDynamicCommands
     */
    @Bean
    public RedisDynamicCommands keyCommands(@Qualifier("onlyWriteNodeURI") RedisURI redisURI) {
        RedisClient redisClient = RedisClient.create(redisURI);
        //为已创建的连接封装线程安全组件
        StatefulRedisConnection statefulRedisConnection = redisClient.connect();
        //创建并返回动态命令客户端实例
        return new RedisCommandFactory(statefulRedisConnection).getCommands(RedisDynamicCommands.class);
    }

    /**
     * 只读从节点客户端
     *
     * @param uriOfSlaves List<RedisURI>
     * @return RedisCommands
     */
    @Bean
    public RedisCommands redisCommands(@Qualifier("onlyReadNodeURI") List<RedisURI> uriOfSlaves) {
        RedisClient redisClient = RedisClient.create();
        //为已创建的连接封装线程安全组件
        StatefulRedisMasterSlaveConnection statefulRedisMasterSlaveConnection = MasterSlave.connect(redisClient, new Utf8StringCodec(), uriOfSlaves);
        //限制读操作只能走从节点
        statefulRedisMasterSlaveConnection.setReadFrom(ReadFrom.SLAVE);
        //创建并返回普通命令客户端
        return statefulRedisMasterSlaveConnection.sync();
    }


}
